package vip.xiaomaoxiaoke.functiondemo.msg.interceptor;

import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
import vip.xiaomaoxiaoke.functiondemo.msg.annotate.LocalMessage;
import vip.xiaomaoxiaoke.functiondemo.msg.message.impl.NativeMethodMessage;
import vip.xiaomaoxiaoke.functiondemo.msg.service.ReliableMethodExecuteService;
import vip.xiaomaoxiaoke.functiondemo.msg.util.SerializeUtil;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 本地消息表注解拦截器
 * <p>
 * 该类实现了 MethodInterceptor 和 BeanPostProcessor 接口，用于拦截带有 @LocalMessage 注解的方法，
 * 并将方法调用信息持久化到可靠的消息队列中。
 */
@Slf4j
public class LocalMessageAnnotationInterceptor implements MethodInterceptor, BeanPostProcessor {

    private final ExpressionParser parser = new SpelExpressionParser();
    private final ReliableMethodExecuteService reliableMessageSendService;
    public static final Map<String, Mk> map = new ConcurrentHashMap<>();
    private final Map<Method, InvokeCacheItem> invokeCache = Maps.newConcurrentMap();

    /**
     * 构造函数
     *
     * @param reliableMessageSendService 可靠消息发送服务
     */
    public LocalMessageAnnotationInterceptor(ReliableMethodExecuteService reliableMessageSendService) {
        this.reliableMessageSendService = Objects.requireNonNull(reliableMessageSendService, "reliableMessageSendService cannot be null");
    }

    /**
     * 内部类，用于存储方法及其所属的 Bean
     */
    @Value
    public static class Mk {
        private final Object bean;
        private final Method method;
    }

    /**
     * Bean 初始化后处理逻辑
     * <p>
     * 扫描所有带有 @LocalMessage 注解的方法，并将其注册到缓存中。
     *
     * @param proxy    当前 Bean 的代理对象
     * @param beanName 当前 Bean 的名称
     * @return 处理后的代理对象
     * @throws BeansException 如果发生错误
     */
    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object proxy, String beanName) throws BeansException {
        // 获取目标类
        Class<?> targetCls = AopUtils.getTargetClass(proxy);
        if (targetCls == null) {
            log.warn("Target class is null for bean: {}", beanName);
            return proxy;
        }

        // 获取所有带有 @LocalMessage 注解的方法
        List<Method> methodsListWithAnnotation = MethodUtils.getMethodsListWithAnnotation(targetCls, LocalMessage.class);

        // 遍历方法并注册到缓存
        for (Method method : methodsListWithAnnotation) {
            if (method.isBridge()) {
                log.warn("Method {} is a bridge method and will be skipped.", method);
                continue;
            }
            String methodKey = generateMethodKey(method);
            map.put(methodKey, new Mk(AopProxyUtils.getSingletonTarget(proxy), method));
            log.info("Registered method: {}", methodKey);
        }

        return proxy;
    }

    /**
     * 拦截方法调用
     *
     * @param methodInvocation 方法调用上下文
     * @return 方法执行结果
     * @throws Throwable 如果方法执行失败
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Method method = methodInvocation.getMethod();
        if (method == null) {
            throw new IllegalArgumentException("Method cannot be null");
        }

        // 解析方法信息并缓存
        InvokeCacheItem invokeCacheItem = this.invokeCache.computeIfAbsent(method, this::parseMethod);

        // 获取方法参数
        Object[] arguments = methodInvocation.getArguments();
        String msgKey = UUID.randomUUID().toString();

        try {
            // 如果存在 msgId 表达式，则使用 SpEL 解析
            if (StringUtils.hasText(invokeCacheItem.msgIdExpression)) {
                StandardEvaluationContext context = new StandardEvaluationContext();
                String[] parameterNames = getParameterNames(method);
                for (int i = 0; i < parameterNames.length; i++) {
                    context.setVariable(parameterNames[i], arguments[i]);
                }
                msgKey = parser.parseExpression(invokeCacheItem.msgIdExpression).getValue(context, String.class);
            }

            // 序列化参数
            String serializedParams = serialize(arguments);

            // 构建消息对象
            NativeMethodMessage nativeMethodMessage = (NativeMethodMessage) new NativeMethodMessage()
                    .setParamValueList(serializedParams)
                    .setMethodKey(generateMethodKey(method))
                    .setMsgId(msgKey)
                    .setOrderly(invokeCacheItem.orderly)
                    .setMaxRetries(invokeCacheItem.maxRetries)
                    .setShardingKey(invokeCacheItem.shardingKey)
                    .setTimeTnterval(invokeCacheItem.timeTnterval);

            // 将消息保存并发送
            reliableMessageSendService.saveAndSend(nativeMethodMessage);
        } catch (Exception e) {
            log.error("Error occurred while processing method invocation: {}", method.getName(), e);
            throw e;
        }

        return null;
    }

    /**
     * 获取方法参数名
     *
     * @param method 方法对象
     * @return 参数名数组
     */
    private String[] getParameterNames(Method method) {
        return Arrays.stream(method.getParameters())
                .map(param -> param.getName()) // 确保编译时启用了 -parameters 选项
                .toArray(String[]::new);
    }

    /**
     * 序列化方法参数
     *
     * @param arguments 方法参数
     * @return 序列化后的字符串
     */
    protected String serialize(Object[] arguments) {
        Map<String, String> result = Maps.newHashMapWithExpectedSize(arguments.length);
        for (int i = 0; i < arguments.length; i++) {
            result.put(String.valueOf(i), SerializeUtil.serialize(arguments[i]));
        }
        return SerializeUtil.serialize(result);
    }

    /**
     * 反序列化方法参数
     *
     * @param str    序列化后的字符串
     * @param method 方法对象
     * @return 反序列化后的参数数组
     */
    private Object[] deserialize(String str, Method method) {
        Map<?, ?> deserialized = SerializeUtil.deserialize(str, Map.class);
        Object[] params = new Object[method.getParameterCount()];

        for (int i = 0; i < method.getParameterCount(); i++) {
            String value = (String) deserialized.get(String.valueOf(i));
            if (value == null) {
                params[i] = null;
            } else {
                params[i] = SerializeUtil.deserialize(value, method.getParameterTypes()[i]);
            }
        }

        return params;
    }

    /**
     * 生成方法签名
     *
     * @param method 方法对象
     * @return 方法签名字符串
     */
    private String generateMethodKey(Method method) {
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        StringBuilder methodSignatureBuilder = new StringBuilder(className)
                .append(".").append(methodName).append("(");

        Class<?>[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            if (i > 0) methodSignatureBuilder.append(", ");
            methodSignatureBuilder.append(parameterTypes[i].getName());
        }
        methodSignatureBuilder.append(")");

        return methodSignatureBuilder.toString();
    }

    /**
     * 解析方法注解信息
     *
     * @param method 方法对象
     * @return 缓存项
     */
    private InvokeCacheItem parseMethod(Method method) {
        LocalMessage localMessage = method.getAnnotation(LocalMessage.class);
        if (localMessage == null) {
            throw new IllegalArgumentException("Method does not have @LocalMessage annotation: " + method.getName());
        }

        return new InvokeCacheItem(
                localMessage.msgIdExpression(),
                localMessage.orderly(),
                localMessage.shardingKey(),
                localMessage.timeTnterval(),
                localMessage.maxRetries()
        );
    }

    /**
     * 缓存项类，用于存储方法注解解析后的数据
     */
    @Value
    private static class InvokeCacheItem {
        private final String msgIdExpression;
        private final boolean orderly;
        private final String shardingKey;
        private final int timeTnterval;
        private final int maxRetries;
    }
}
